package netty.io.demo.NIO.handle;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * nio 服务端 通信
 *
 * @author lihh
 */
public class NIOServerHandle implements Runnable {
  private volatile boolean started;
  private Selector selector;
  private ServerSocketChannel serverSocketChannel;
  
  public NIOServerHandle(InetSocketAddress inetSocketAddress) throws IOException {
    
    // 创建 Selector
    this.selector = Selector.open();
    // 创建 Server Selector
    this.serverSocketChannel = ServerSocketChannel.open();
    // 设置 非阻塞模式
    this.serverSocketChannel.configureBlocking(false);
    // 绑定 地址
    this.serverSocketChannel.socket().bind(inetSocketAddress);
    
    // 注册 accept 事件
    this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
    this.started = true;
    
    System.out.println("服务已启动, 端口号：" + inetSocketAddress.getPort());
  }
  
  public void stop() {
    this.started = false;
  }
  
  @Override
  public void run() {
    while (this.started) {
      try {
        // 此处 会进行阻塞，只有 有事件响应后 才不会阻塞
        this.selector.select();
        
        Set<SelectionKey> selectionKeys = this.selector.selectedKeys();
        Iterator<SelectionKey> it = selectionKeys.iterator();
        
        SelectionKey key = null;
        // 循环 判断事件
        while (it.hasNext()) {
          key = it.next();
          it.remove();
          
          this.handleInput(key);
        }
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
    
    if (this.selector != null) {
      try {
        this.selector.close();
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }
  
  /**
   * 处理 事件
   *
   * @param key 响应的key
   * @author lihh
   */
  private void handleInput(SelectionKey key) throws IOException {
    if (!key.isValid()) {
      return;
    }
    
    // 判断是否请求过来
    if (key.isAcceptable()) {
      ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
      SocketChannel sc = ssc.accept();
      
      System.out.println("-- 执行到这里 已经连接成功 --");
      
      sc.configureBlocking(false);
      // 注册 read 事件
      sc.register(this.selector, SelectionKey.OP_READ);
      
      // 这里 处理 read 事件
    } else if (key.isReadable()) {
      
      System.out.println("-- 执行到这里 开始处理 read 事件 --");
      
      SocketChannel sc = (SocketChannel) key.channel();
      //创建ByteBuffer，并开辟一个1M的缓冲区
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      //读取请求码流，返回读取到的字节数
      int readBytes = sc.read(buffer);
      
      if (readBytes > 0) {
        //将缓冲区当前的limit设置为position,position=0，
        // 用于后续对缓冲区的读取操作
        buffer.flip();
        //根据缓冲区可读字节数创建字节数组
        byte[] bytes = new byte[buffer.remaining()];
        //将缓冲区可读字节数组复制到新建的数组中
        buffer.get(bytes);
        String message = new String(bytes, StandardCharsets.UTF_8);
        System.out.println("服务器收到消息：" + message);
        //处理数据
        String result = "服务器响应消息：" + message;
        //发送应答消息
        doWrite(sc, result);
      } else if (readBytes < 0) {
        sc.close();
        key.cancel();
      }
    }
  }
  
  //发送应答消息
  private void doWrite(SocketChannel channel, String response) throws IOException {
    //将消息编码为字节数组
    byte[] bytes = response.getBytes();
    //根据数组容量创建ByteBuffer
    ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
    //将字节数组复制到缓冲区
    writeBuffer.put(bytes);
    //flip操作
    writeBuffer.flip();
    //发送缓冲区的字节数组
    channel.write(writeBuffer);
  }
}
